package route

import (
	"file-system/internal/handler"
	"fmt"
	"github.com/gin-gonic/gin"
	"io/ioutil"
	"net/http"
	"os"
	"path"
	"path/filepath"
	"strings"
)

type Handler struct {
}

func NewHandler() (handler.RouteRegister, error) {
	return &Handler{}, nil
}

func (h *Handler) ApplyRoute(r *gin.Engine) {
	r.GET("/dirs", GetDir)
	r.GET("/file", GetFile)
	r.POST("/create", CreateDir)
	r.POST("/delete", Delete)
	r.POST("/is-empty", IsEmptyDir)
	r.POST("/upload", Upload)
	r.GET("/download", Download)
}

type Dir struct {
	IsDir bool   `json:"is_dir"`
	Name  string `json:"name"`
	Size  int64  `json:"size"`
	Time  int64  `json:"time"`
	Path  string `json:"path"`
	Type  string `json:"type"`
}

type UploadInput struct {
	Dir  string `json:"dir"`
	Name string `json:"name"`
}

func Upload(c *gin.Context) {
	input := &UploadInput{
		Dir:  c.PostForm("dir"),
		Name: c.PostForm("name"),
	}
	//err := c.BindJSON(&input)
	//if err != nil {
	//	c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"message": "dir can not empty"})
	//	return
	//}

	if input.Dir == "" {
		c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"message": "dir can not empty"})
		return
	}
	file, err := c.FormFile("file")
	if err != nil {
		c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"message": "read file error: " + err.Error()})
		return
	}
	dist := input.Dir + "/" + input.Name
	err = c.SaveUploadedFile(file, dist)
	if err != nil {
		c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"message": "save file error: " + err.Error()})
		return
	}
	c.JSON(http.StatusOK, gin.H{"message": "success"})
}

func Download(c *gin.Context) {
	dir := c.Query("dir")
	if dir == "" {
		c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"message": "dir can not empty"})
		return
	}
	c.Writer.Header().Add("Content-Disposition", fmt.Sprintf("attachment; filename=%s", filepath.Base(dir))) //fmt.Sprintf("attachment; filename=%s", filename)对下载的文件重命名
	c.Writer.Header().Add("Content-Type", "application/octet-stream")
	c.File(dir)
}

type DirInput struct {
	Dir string `json:"dir"`
}

func GetDir(c *gin.Context) {
	dir := c.Query("path")

	if dir == "" {
		dir = "/"
	}
	open, err := ioutil.ReadDir(dir)
	if err != nil {
		c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"message": "open file error"})
		return
	}
	files := make([]*Dir, 0, len(open))
	for _, file := range open {
		d := &Dir{
			IsDir: false,
			Name:  file.Name(),
			Time:  file.ModTime().Unix(),
			Size:  file.Size(),
			Path:  dir + "/" + file.Name(),
			Type:  "",
		}

		if dir == "/" {
			d.Path = "/" + file.Name()
		}
		if file.IsDir() {
			d.IsDir = true
		} else {
			d.Type = path.Ext(file.Name())
		}
		files = append(files, d)
	}
	c.JSON(http.StatusOK, files)
}

func GetFile(c *gin.Context) {
	dir := c.Query("path")
	if dir == "" {
		// TODO handle error
		return
	}
	c.File(dir)
	return
}

func CreateDir(c *gin.Context) {
	var input *UploadInput
	_ = c.BindJSON(&input)
	if input.Dir == "" {
		c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"message": "dir can not empty"})
		return
	}
	if input.Name == "" {
		c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"message": "name can not empty"})
		return
	}
	err := os.Mkdir(input.Dir+"/"+input.Name, os.ModePerm)
	if err != nil {
		if strings.Contains(err.Error(), "Cannot create a file when that file already exists") {
			c.AbortWithStatusJSON(http.StatusOK, gin.H{"message": "dir already exists"})
			return
		}
		c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"message": "create dir error" + err.Error()})
		return
	}
	c.JSON(http.StatusOK, gin.H{"message": "success"})
}

func Delete(c *gin.Context) {
	var input *DirInput
	_ = c.BindJSON(&input)
	if input.Dir == "" {
		c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"message": "path can not empty"})
		return
	}
	err := os.RemoveAll(input.Dir)
	if err != nil {
		c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"message": "delete file error"})
		return
	}
	c.JSON(http.StatusOK, gin.H{"message": "success"})
}

func IsEmptyDir(c *gin.Context) {
	var input *DirInput
	_ = c.BindJSON(&input)
	if input.Dir == "" {
		c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"message": "path can not empty"})
		return
	}
	dir, err := ioutil.ReadDir(input.Dir)
	if err != nil {
		c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"message": "read dir error: " + err.Error()})
		return
	}
	var flag bool
	if len(dir) == 0 {
		flag = true
	}
	c.JSON(http.StatusOK, gin.H{"data": flag})
}
